home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr05 / xnot12a.zip / SEARCH.C < prev    next >
C/C++ Source or Header  |  1993-05-20  |  15KB  |  738 lines

  1. #include "jam.h"
  2. /*
  3.  *        Search commands.
  4.  * The functions in this file implement the
  5.  * search commands (both plain and incremental searches
  6.  * are supported) and the query-replace command.
  7.  *
  8.  * The plain old search code is part of the original
  9.  * MicroEMACS "distribution". The incremental search code,
  10.  * and the query-replace code, is by Rich Ellison.
  11.  */
  12. #include "def.h"
  13. #include "keyname.h"
  14. #ifndef NO_MACRO
  15. #include "macro.h"
  16. #endif
  17.  
  18. static VOID rn_(is_cpush,(int cmd));
  19. static VOID rn_(is_lpush,(void)); 
  20. static VOID rn_(is_pop,(void));
  21. static int rn_(is_peek,(void));
  22. static VOID rn_(is_undo,(int *pptr, int *dir));
  23. static int rn_(is_find,(int dir)); 
  24. static VOID rn_(is_prompt,(int dir, int flag, int success));
  25. static VOID rn_(is_dspl,(char *prompt, int flag));
  26. static int rn_(eq,(int bc, int pc));
  27.  
  28. /* JAM - reuseable strings
  29. */
  30. BOOL isearching = FALSE;
  31. static char *sfailed = "Search failed: \"%s\"";
  32.  
  33. /* end JAM */
  34.  
  35. #define SRCH_BEGIN    (0)            /* Search sub-codes.    */
  36. #define SRCH_FORW    (-1)
  37. #define SRCH_BACK    (-2)
  38. #define SRCH_NOPR    (-3)
  39. #define SRCH_ACCM    (-4)
  40. #define SRCH_MARK    (-5)
  41.  
  42. typedef struct    {
  43.     int    s_code;
  44.     LINE    *s_dotp;
  45.     int    s_doto;
  46. }    SRCHCOM;
  47.  
  48. static    SRCHCOM cmds[NSRCH];
  49. static    int    cip;
  50.  
  51. static int srch_lastdir = SRCH_NOPR;        /* Last search flags.    */
  52.  
  53. /* Routine to push 2 search commands into input
  54. * stream to effect null-motion search (refresh hack)
  55. * This is ughly (JAM)
  56. */
  57. void jumpSearch()
  58. {
  59.   if (srch_lastdir == SRCH_BACK)    /* was backward, do ^S ^R */
  60.     {    
  61.       AddKchar(CCHR('S'));
  62.       AddKchar(CCHR('R'));
  63.     }
  64.   else                    /* was forward, do ^R ^S */
  65.     {
  66.       AddKchar(CCHR('R'));
  67.       AddKchar(CCHR('S'));
  68.     }
  69. }
  70.  
  71. /*
  72.  * Search forward.
  73.  * Get a search string from the user, and search for it,
  74.  * starting at ".". If found, "." gets moved to just after the
  75.  * matched characters, and display does all the hard stuff.
  76.  * If not found, it just prints a message.
  77.  */
  78. /*ARGSUSED*/
  79. forwsearch(f, n)
  80. int f, n;
  81. {
  82.     register int    s;
  83.  
  84.     if ((s=readpattern("Search")) != TRUE)
  85.         return s;
  86.     if (forwsrch() == FALSE) {
  87.         ewprintf(sfailed, pat);
  88.         return FALSE;
  89.     }
  90.     srch_lastdir = SRCH_FORW;
  91.     return TRUE;
  92. }
  93.  
  94. /*
  95.  * Reverse search.
  96.  * Get a search string from the     user, and search, starting at "."
  97.  * and proceeding toward the front of the buffer. If found "." is left
  98.  * pointing at the first character of the pattern [the last character that
  99.  * was matched].
  100.  */
  101. /*ARGSUSED*/
  102. backsearch(f, n)
  103. int f, n;
  104. {
  105.     register int    s;
  106.  
  107.     if ((s=readpattern("Search backward")) != TRUE)
  108.         return (s);
  109.     if (backsrch() == FALSE) {
  110.         ewprintf(sfailed, pat);
  111.         return FALSE;
  112.     }
  113.     srch_lastdir = SRCH_BACK;
  114.     return TRUE;
  115. }
  116.  
  117. /*
  118.  * Search again, using the same search string
  119.  * and direction as the last search command. The direction
  120.  * has been saved in "srch_lastdir", so you know which way
  121.  * to go.
  122.  */
  123. /*ARGSUSED*/
  124. searchagain(f, n)
  125. int f, n;
  126. {
  127.     if (srch_lastdir == SRCH_FORW) {
  128.         if (forwsrch() == FALSE) {
  129.             ewprintf(sfailed, pat);
  130.             return FALSE;
  131.         }
  132.         return TRUE;
  133.     }
  134.     if (srch_lastdir == SRCH_BACK) {
  135.         if (backsrch() == FALSE) {
  136.             ewprintf(sfailed, pat);
  137.             return FALSE;
  138.         }
  139.         return TRUE;
  140.     }
  141.     ewprintf("No last search");
  142.     return FALSE;
  143. }
  144.  
  145. /*
  146.  * Use incremental searching, initially in the forward direction.
  147.  * isearch ignores any explicit arguments.
  148.  */
  149. /*ARGSUSED*/
  150. forwisearch(f, n)
  151. int f, n;
  152. {
  153.     return isearch(SRCH_FORW);
  154. }
  155.  
  156. /*
  157.  * Use incremental searching, initially in the reverse direction.
  158.  * isearch ignores any explicit arguments.
  159.  */
  160. /*ARGSUSED*/
  161. backisearch(f, n)
  162. int f, n;
  163. {
  164.     return isearch(SRCH_BACK);
  165. }
  166.  
  167. /*
  168.  * Incremental Search.
  169.  *    dir is used as the initial direction to search.
  170.  *    ^S    switch direction to forward
  171.  *    ^R    switch direction to reverse
  172.  *    ^Q    quote next character (allows searching for ^N etc.)
  173.  *    <ESC>    exit from Isearch
  174.  *    <DEL>    undoes last character typed. (tricky job to do this correctly).
  175.  *    other ^ exit search, don't set mark
  176.  *    else    accumulate into search string
  177.  */
  178. isearch(dir) 
  179. int dir;
  180. {
  181.     register int    c;
  182.     register LINE    *clp;
  183.     register int    cbo;
  184.     register int    success;
  185.     int        pptr;
  186.     char        opat[NPAT];
  187.  
  188. #ifndef NO_MACRO
  189.     if(macrodef) {
  190.         ewprintf("Can't isearch in macro");
  191.         return FALSE;
  192.     }
  193. #endif
  194.     for (cip=0; cip<NSRCH; cip++)
  195.         cmds[cip].s_code = SRCH_NOPR;
  196.     (VOID) strcpy(opat, pat);
  197.     cip = 0;
  198.     pptr = -1;
  199.     clp = curwp->w_dotp;
  200.     cbo = curwp->w_doto;
  201.     is_lpush();
  202.     is_cpush(SRCH_BEGIN);
  203.     success = TRUE;
  204.     is_prompt(dir, TRUE, success);
  205.     for (;;) {
  206.             eprompting = isearching = TRUE;
  207.         update();
  208.  
  209.         c = getkey(FALSE);
  210.         if (!(c = handleKchar(c)))
  211.           {
  212.             srch_lastdir = dir;
  213.             curwp->w_markp = clp;
  214.             curwp->w_marko = cbo;
  215.             curwp->w_flag |= WFMOVE;
  216.                 eprompting = isearching = FALSE;
  217.             return ABORT;
  218.               }
  219.  
  220.         switch (c) {
  221.         case CCHR('['):
  222.             srch_lastdir = dir;
  223.             curwp->w_markp = clp;
  224.             curwp->w_marko = cbo;
  225.             ewprintf(Markset);
  226.                     eprompting = isearching = FALSE;
  227.             return (TRUE);
  228.  
  229.         case CCHR('G'):
  230.             if (success != TRUE) {
  231.                 while (is_peek() == SRCH_ACCM)
  232.                     is_undo(&pptr, &dir);
  233.                 success = TRUE;
  234.                 is_prompt(dir, pptr < 0, success);
  235.                 break;
  236.             }
  237.             curwp->w_dotp = clp;
  238.             curwp->w_doto = cbo;
  239.             curwp->w_flag |= WFMOVE;
  240.             srch_lastdir = dir;
  241.             (VOID) ctrlg(FFRAND, 0);
  242.             (VOID) strcpy(pat, opat);
  243.                     eprompting = isearching = FALSE;
  244.             return ABORT;
  245.  
  246.         case CCHR(']'):
  247.         case CCHR('S'):
  248.             if (dir == SRCH_BACK) {
  249.                 dir = SRCH_FORW;
  250.                 is_lpush();
  251.                 is_cpush(SRCH_FORW);
  252.                 success = TRUE;
  253.             }
  254.             if (success==FALSE && dir==SRCH_FORW)
  255.               break;
  256.             is_lpush();
  257.             pptr = strlen(pat);
  258.             (VOID) forwchar(FFRAND, 1);
  259.             if (is_find(SRCH_FORW) != FALSE) 
  260.                           is_cpush(SRCH_MARK);
  261.             else {
  262.                 (VOID) backchar(FFRAND, 1);
  263.                 ttbeep();
  264.                 success = FALSE;
  265.             }
  266.             is_prompt(dir, pptr < 0, success);
  267.             break;
  268.  
  269.         case CCHR('R'):
  270.             if (dir == SRCH_FORW) {
  271.                 dir = SRCH_BACK;
  272.                 is_lpush();
  273.                 is_cpush(SRCH_BACK);
  274.                 success = TRUE;
  275.             }
  276.             if (success==FALSE && dir==SRCH_BACK)
  277.               break;
  278.             is_lpush();
  279.             pptr = strlen(pat);
  280.             (VOID) backchar(FFRAND, 1);
  281.             if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
  282.             else {
  283.                 (VOID) forwchar(FFRAND, 1);
  284.                 ttbeep();
  285.                 success = FALSE;
  286.             }
  287.             is_prompt(dir, pptr < 0, success);
  288.             break;
  289.  
  290.                 case KDELETE:
  291.         case CCHR('H'):
  292.         case CCHR('?'):
  293.             is_undo(&pptr, &dir);
  294.             if (is_peek() != SRCH_ACCM) success = TRUE;
  295.             is_prompt(dir, pptr < 0, success);
  296.             break;
  297.  
  298.         case CCHR('\\'):
  299.         case CCHR('Q'):
  300.             c = (char) getkey(FALSE);
  301.             goto  addchar;
  302.         case CCHR('M'):
  303.             c = CCHR('J');
  304.             goto  addchar;
  305.  
  306.         default:
  307.             if (ISCTRL(c)) {
  308.                 ungetkey(c);
  309.                 curwp->w_markp = clp;
  310.                 curwp->w_marko = cbo;
  311.                 ewprintf(Markset);
  312.                 curwp->w_flag |= WFMOVE;
  313.                             eprompting = isearching = FALSE;
  314.                 return    TRUE;
  315.             }    /* and continue */
  316.         case CCHR('I'):
  317.         case CCHR('J'):
  318.         addchar:
  319.             if (pptr == -1)
  320.                 pptr = 0;
  321.             if (pptr == 0)
  322.                 success = TRUE;
  323.             pat[pptr++] = (char)c;
  324.             if (pptr == NPAT) {
  325.                 ewprintf("Pattern too long");
  326.                             eprompting = isearching = FALSE;
  327.                 return FALSE;
  328.             }
  329.             pat[pptr] = '\0';
  330.             is_lpush();
  331.             if (success != FALSE) {
  332.                 if (is_find(dir) != FALSE)
  333.                     is_cpush(c);
  334.                 else {
  335.                     success = FALSE;
  336.                     ttbeep();
  337.                     is_cpush(SRCH_ACCM);
  338.                 }
  339.             } else
  340.                 is_cpush(SRCH_ACCM);
  341.             is_prompt(dir, FALSE, success);
  342.         }
  343.     }
  344.     /*NOTREACHED*/
  345. }
  346.  
  347. static VOID is_cpush(cmd) 
  348. register int cmd; 
  349. {
  350.     if (++cip >= NSRCH)
  351.         cip = 0;
  352.     cmds[cip].s_code = cmd;
  353. }
  354.  
  355. static VOID is_lpush() 
  356. {
  357.     register int    ctp;
  358.  
  359.     ctp = cip+1;
  360.     if (ctp >= NSRCH)
  361.         ctp = 0;
  362.     cmds[ctp].s_code = SRCH_NOPR;
  363.     cmds[ctp].s_doto = curwp->w_doto;
  364.     cmds[ctp].s_dotp = curwp->w_dotp;
  365. }
  366.  
  367. static VOID is_pop() 
  368. {
  369.     if (cmds[cip].s_code != SRCH_NOPR) {
  370.         curwp->w_doto  = cmds[cip].s_doto;
  371.         curwp->w_dotp  = cmds[cip].s_dotp;
  372.         curwp->w_flag |= WFMOVE;
  373.         cmds[cip].s_code = SRCH_NOPR;
  374.     }
  375.     if (--cip <= 0)
  376.         cip = NSRCH-1;
  377. }
  378.  
  379. static int is_peek() 
  380. {
  381.     return cmds[cip].s_code;
  382. }
  383.  
  384. /* this used to always return TRUE (the return value was checked) 
  385. */
  386. static VOID is_undo(pptr, dir) 
  387. register int *pptr; 
  388. register int *dir; 
  389. {
  390.     register int    redo = FALSE ;
  391.  
  392.     switch (cmds[cip].s_code) {
  393.     case SRCH_BEGIN:
  394.     case SRCH_NOPR:
  395.         *pptr = -1;
  396.     case SRCH_MARK:
  397.         break;
  398.  
  399.     case SRCH_FORW:
  400.         *dir = SRCH_BACK;
  401.         redo = TRUE;
  402.         break;
  403.  
  404.     case SRCH_BACK:
  405.         *dir = SRCH_FORW;
  406.         redo = TRUE;
  407.         break;
  408.  
  409.     case SRCH_ACCM:
  410.     default:
  411.         *pptr -= 1;
  412.         if (*pptr < 0)
  413.             *pptr = 0;
  414.         pat[*pptr] = '\0';
  415.         break;
  416.     }
  417.     is_pop();
  418.     if (redo) is_undo(pptr, dir);
  419. }
  420.  
  421. static int is_find(dir) 
  422. register int dir; 
  423. {
  424.     register int    plen, odoto;
  425.     register LINE    *odotp ;
  426.  
  427.     odoto = curwp->w_doto;
  428.     odotp = curwp->w_dotp;
  429.     plen = strlen(pat);
  430.     if (plen != 0) {
  431.         if (dir==SRCH_FORW) {
  432.             (VOID) backchar(FFARG | FFRAND, plen);
  433.             if (forwsrch() == FALSE) {
  434.                 curwp->w_doto = odoto;
  435.                 curwp->w_dotp = odotp;
  436.                 return FALSE;
  437.             }
  438.             return TRUE;
  439.         }
  440.         if (dir==SRCH_BACK) {
  441.             (VOID) forwchar(FFARG | FFRAND, plen);
  442.             if (backsrch() == FALSE) {
  443.                 curwp->w_doto = odoto;
  444.                 curwp->w_dotp = odotp;
  445.                 return FALSE;
  446.             }
  447.             return TRUE;
  448.         }
  449.         ewprintf("bad call to is_find");
  450.         return FALSE;
  451.     }
  452.     return FALSE;
  453. }
  454.  
  455. /*
  456.  * If called with "dir" not one of SRCH_FORW
  457.  * or SRCH_BACK, this routine used to print an error
  458.  * message. It also used to return TRUE or FALSE,
  459.  * depending on if it liked the "dir". However, none
  460.  * of the callers looked at the status, so I just
  461.  * made the checking vanish.
  462.  */
  463. static VOID is_prompt(dir, flag, success) 
  464. int dir, flag, success;
  465. {
  466.     if (dir == SRCH_FORW) {
  467.         if (success != FALSE)
  468.             is_dspl("I-search", flag);
  469.         else
  470.             is_dspl("Failing I-search", flag);
  471.     } else if (dir == SRCH_BACK) {
  472.         if (success != FALSE)
  473.             is_dspl("I-search backward", flag);
  474.         else
  475.             is_dspl("Failing I-search backward", flag);
  476.     } else ewprintf("Broken call to is_prompt");
  477. }
  478.  
  479. /*
  480.  * Prompt writing routine for the incremental search.
  481.  * The "prompt" is just a string. The "flag" determines
  482.  * whether pat should be printed.
  483.  */
  484. static VOID is_dspl(prompt, flag)
  485. char *prompt;
  486. int flag;
  487. {
  488.     if (flag != FALSE)
  489.         ewprintf("%s: ", prompt);
  490.     else
  491.         ewprintf("%s: %s", prompt, pat);
  492. }
  493.  
  494. /*
  495.  * Query Replace.
  496.  *    Replace strings selectively.  Does a search and replace operation.
  497.  */
  498. /*ARGSUSED*/
  499. queryrepl(f, n)
  500. int f, n;
  501. {
  502.     register int    s;
  503.     register int    rcnt = 0;    /* Replacements made so far    */
  504.     register int    plen;        /* length of found string    */
  505.     char        news[NPAT];    /* replacement string        */
  506.  
  507. #ifndef NO_MACRO
  508.     if(macrodef) {
  509.         ewprintf("Can't query replace in macro");
  510.         return FALSE;
  511.     }
  512. #endif
  513.     if ((s=readpattern("Query replace")) != TRUE)
  514.         return (s);
  515.     if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
  516.         return (s);
  517.     if (s == FALSE)
  518.         news[0] = '\0';
  519.     ewprintf("Query replacing %s with %s:", pat, news);
  520.     plen = strlen(pat);
  521.  
  522.     /*
  523.      * Search forward repeatedly, checking each time whether to insert
  524.      * or not.  The "!" case makes the check always true, so it gets put
  525.      * into a tighter loop for efficiency.
  526.      */
  527.  
  528.     while (forwsrch() == TRUE) {
  529.     retry:
  530.         update();
  531.         switch (getkey(FALSE)) {
  532.         case 'y':
  533.         case 'Y':
  534.         case ' ':
  535.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  536.                 return (FALSE);
  537.             rcnt++;
  538.             break;
  539.  
  540.         case '.':
  541.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  542.                 return (FALSE);
  543.             rcnt++;
  544.             goto stopsearch;
  545.  
  546.         case CCHR('G'): /* ^G or ESC */
  547.             (VOID) ctrlg(FFRAND, 0);
  548.         case CCHR('['):
  549.             goto stopsearch;
  550.  
  551.         case '!':
  552.             do {
  553.                 if (lreplace((RSIZE) plen, news, f) == FALSE)
  554.                     return (FALSE);
  555.                 rcnt++;
  556.             } while (forwsrch() == TRUE);
  557.             goto stopsearch;
  558.  
  559.                 case 'n':
  560.         case 'N':
  561.         case CCHR('H'):
  562.         case CCHR('?'):        /* To not replace */
  563.             break;
  564.  
  565.         default:
  566. ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
  567.             goto retry;
  568.         }
  569.     }
  570. stopsearch:
  571.     curwp->w_flag |= WFHARD;
  572.     update();
  573.     if (rcnt == 0)
  574.         ewprintf("(No replacements done)");
  575.     else if (rcnt == 1)
  576.         ewprintf("(1 replacement done)");
  577.     else
  578.         ewprintf("(%d replacements done)", rcnt);
  579.     return TRUE;
  580. }
  581.  
  582. /*
  583.  * This routine does the real work of a
  584.  * forward search. The pattern is sitting in the external
  585.  * variable "pat". If found, dot is updated, the window system
  586.  * is notified of the change, and TRUE is returned. If the
  587.  * string isn't found, FALSE is returned.
  588.  */
  589. forwsrch() 
  590. {
  591.     register LINE    *clp;
  592.     register int    cbo;
  593.     register LINE    *tlp;
  594.     register int    tbo;
  595.     char        *pp;
  596.     register int    c;
  597.  
  598.     clp = curwp->w_dotp;
  599.     cbo = curwp->w_doto;
  600.     for(;;) {
  601.         if (cbo == llength(clp)) {
  602.             if((clp = lforw(clp)) == curbp->b_linep) break;
  603.             cbo = 0;
  604.             c = CCHR('J');
  605.         } else
  606.             c = lgetc(clp, cbo++);
  607.         if (eq(c, pat[0]) != FALSE) {
  608.             tlp = clp;
  609.             tbo = cbo;
  610.             pp  = &pat[1];
  611.             while (*pp != 0) {
  612.                 if (tbo == llength(tlp)) {
  613.                     tlp = lforw(tlp);
  614.                     if (tlp == curbp->b_linep)
  615.                         goto fail;
  616.                     tbo = 0;
  617.                     c = CCHR('J');
  618.                 } else
  619.                     c = lgetc(tlp, tbo++);
  620.                 if (eq(c, *pp++) == FALSE)
  621.                     goto fail;
  622.             }
  623.             curwp->w_dotp  = tlp;
  624.             curwp->w_doto  = tbo;
  625.             curwp->w_flag |= WFMOVE;
  626.             return TRUE;
  627.         }
  628.     fail:    ;
  629.     }
  630.     return FALSE;
  631. }
  632.  
  633. /*
  634.  * This routine does the real work of a
  635.  * backward search. The pattern is sitting in the external
  636.  * variable "pat". If found, dot is updated, the window system
  637.  * is notified of the change, and TRUE is returned. If the
  638.  * string isn't found, FALSE is returned.
  639.  */
  640. backsrch() 
  641. {
  642.     register LINE    *clp;
  643.     register int    cbo;
  644.     register LINE    *tlp;
  645.     register int    tbo;
  646.     register int    c;
  647.     register char    *epp;
  648.     register char    *pp;
  649.  
  650.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  651.         ;
  652.     clp = curwp->w_dotp;
  653.     cbo = curwp->w_doto;
  654.     for (;;) {
  655.         if (cbo == 0) {
  656.             clp = lback(clp);
  657.             if (clp == curbp->b_linep)
  658.                 return FALSE;
  659.             cbo = llength(clp)+1;
  660.         }
  661.         if (--cbo == llength(clp))
  662.             c = CCHR('J');
  663.         else
  664.             c = lgetc(clp,cbo);
  665.         if (eq(c, *epp) != FALSE) {
  666.             tlp = clp;
  667.             tbo = cbo;
  668.             pp  = epp;
  669.             while (pp != &pat[0]) {
  670.                 if (tbo == 0) {
  671.                     tlp = lback(tlp);
  672.                     if (tlp == curbp->b_linep)
  673.                         goto fail;
  674.                     tbo = llength(tlp)+1;
  675.                 }
  676.                 if (--tbo == llength(tlp))
  677.                     c = CCHR('J');
  678.                 else
  679.                     c = lgetc(tlp,tbo);
  680.                 if (eq(c, *--pp) == FALSE)
  681.                     goto fail;
  682.             }
  683.             curwp->w_dotp  = tlp;
  684.             curwp->w_doto  = tbo;
  685.             curwp->w_flag |= WFMOVE;
  686.             return TRUE;
  687.         }
  688.     fail:    ;
  689.     }
  690.     /*NOTREACHED*/
  691. }
  692.  
  693. /*
  694.  * Compare two characters.
  695.  * The "bc" comes from the buffer.
  696.  * It has its case folded out. The
  697.  * "pc" is from the pattern.
  698.  */
  699. static int eq(bc, pc)
  700. register int bc, pc;
  701. {
  702.   bc = CHARMASK(bc);
  703.   pc = CHARMASK(pc);
  704.   if (bc == pc) 
  705.     return TRUE;
  706.   if (ISUPPER(bc)) 
  707.     return TOLOWER(bc) == pc;
  708.   if (ISUPPER(pc)) 
  709.     return bc == TOLOWER(pc);
  710.   return FALSE;
  711. }
  712.  
  713. /*
  714.  * Read a pattern.
  715.  * Stash it in the external variable "pat". The "pat" is
  716.  * not updated if the user types in an empty line. If the user typed
  717.  * an empty line, and there is no old pattern, it is an error.
  718.  * Display the old pattern, in the style of Jeff Lomicka. There is
  719.  * some do-it-yourself control expansion.
  720.  */
  721. readpattern(prompt) 
  722. char *prompt; 
  723. {
  724.     register int    s;
  725.     char        tpat[NPAT];
  726.  
  727.     if (tpat[0] == '\0') 
  728.       s = ereply("%s: ", tpat, NPAT, prompt);
  729.     else 
  730.       s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
  731.  
  732.     if (s == TRUE)                /* Specified        */
  733.       (VOID) strcpy(pat, tpat);
  734.     else if (s==FALSE && pat[0]!=0)        /* CR, but old one    */
  735.       s = TRUE;
  736.     return s;
  737. }
  738.